package com.h.forum.services.impl;

import com.h.forum.common.constant.Constants;
import com.h.forum.common.enums.ResultCode;
import com.h.forum.common.utils.*;
import com.h.forum.dao.UserMapper;
import com.h.forum.exception.ApplicationException;
import com.h.forum.pojo.model.User;
import com.h.forum.pojo.request.UserLoginRequest;
import com.h.forum.pojo.request.UserModPasswordRequest;
import com.h.forum.pojo.request.UserRegisterRequest;
import com.h.forum.pojo.response.AppResult;
import com.h.forum.pojo.response.UserInfoResponse;
import com.h.forum.pojo.response.UserLoginResponse;
import com.h.forum.services.IUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    @Resource
    private UserMapper userMapper;

    /**
     * 注册用户
     * @param user 注册时候的信息
     */
    @Override
    public void createNormalUser(User user) {
        // 1、非空校验
        if (user == null
                || StringUtil.isEmpty(user.getUserName())
                || StringUtil.isEmpty(user.getNickName())
                || StringUtil.isEmpty(user.getPassWord())
                || StringUtil.isEmpty(user.getSalt())) {
            // 打印日志，为空
            log.error(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常给到前端
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 2、查询当前注册的用户是否存在
        User existsUser  = userMapper.selectByUserName(user.getUserName());
        // 查询是否存在
        if (existsUser != null) {
            //非空，打印异常，并且抛出
            log.error(ResultCode.FAILED_USER_EXISTS.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_EXISTS));
        }

        // 3、到这里说明是一个新用户，那么进行处理，并且插入
        if (user.getGender() == null) {
            user.setGender((byte)2);
        }
        user.setIsAdmin((byte)0);
        user.setArticleCount(0);
        user.setState((byte)0);
        user.setDeleteState((byte)0);
        Date date = new Date();
        user.setCreateTime(date);
        user.setUpdateTime(date);

        // 4、写入数据库
        int row = userMapper.insertSelective(user);// 这里动态写入，这里返回的是影响的行数

        if (row != 1) {
            // 说明错误，打印日志，抛出异常
            log.error(ResultCode.FAILED_CREATE.toString());

            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_CREATE));
        }
        // 打印日志，说明注册成功
        log.info("新用户新增成功 userName: " + user.getUserName() + ".");
    }

    /**
     * 查询用户信息
     * @param userName
     * @return
     */
    @Override
    public User selectByUserName(String userName) {
        // 非空校验
        if (StringUtil.isEmpty(userName)) {
            // 打印日志
            log.error(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        return userMapper.selectByUserName(userName);
    }

    /**
     * 用户登录
     * @param userLoginRequest
     * @return
     */
    @Override
    public UserLoginResponse login(UserLoginRequest userLoginRequest) {
        // 1、校验是否为空信息
        if (StringUtil.isEmpty(userLoginRequest.getUserName())
                || StringUtil.isEmpty(userLoginRequest.getPassWord())) {
            // 打印日志
            log.error(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 2、根据用户名进行查询用户信息
        User user = selectByUserName(userLoginRequest.getUserName());

        // 3、判断这个 user 是否存在
        if (user == null) {
            // 打印日志
            log.warn(ResultCode.FAILED_LOGIN.toString() + ", userName = " + userLoginRequest.getUserName());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_LOGIN));
        }

        // 4、对密码进行校验
        // 先将这个密码和user中的盐值进行加密处理
        String entryPassword = MD5Util.md5Salt(userLoginRequest.getPassWord(),
                user.getSalt());
        // 5、用加密后的字符串和user中的字符串进行比较
        if (!user.getPassWord().equals(entryPassword)) {
            // 打印日志
            log.warn(ResultCode.FAILED_LOGIN.toString() +
                    ", 密码错误 userName = " + userLoginRequest.getUserName());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_LOGIN));
        }

        // 6、对这个登录的用户进行一个创建Jwt令牌的操作
        Map<String,Object> claims = new HashMap<>();
        claims.put("id", user.getId());
        claims.put("userName", user.getUserName());
        // 7、将要创建令牌的信息进行存储
        String jwt = JWTUtil.getJwt(claims);
        return new UserLoginResponse(jwt);
    }

    /**
     * 根据Id进行查询用户信息
     * @param id
     * @return
     */
    @Override
    public UserInfoResponse selectById(Long id) {
        // 1、非空校验
        if (id == null) {
            // 打印日志
            log.error(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 2、调用DAO查询数据库并获取对象
        User user = userMapper.selectByPrimaryKey(id);

        return BeanTransUnits.trans(user);
    }

    /**
     * 根据用户Id将对应的帖子数量加一
     * @param id
     */
    @Override
    public void addOneArticleCountById(Long id) {
        // 1、校验信息
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_BOARD_ARTICLE_COUNT));
        }

        // 2、根据用户Id，在数据库中查询用户的对应的信息
        User user = userMapper.selectByPrimaryKey(id);
        // 3、进行判断是否是存在这个用户
        if (user == null) {
            // 打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString() + ", user id = " + id);
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_NOT_EXISTS));
        }

        // 4、根据user信息创建一个需要更新的信息
        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setArticleCount(user.getArticleCount() + 1);

        // 5、将这个需要更新的信息放入到对应的表中，进行更新操作
        int row = userMapper.updateByPrimaryKeySelective(updateUser);
        // 6、判断受影响的行数是否是1
        if (row != 1) {
            // 打印日志
            log.warn(ResultCode.FAILED_USER_ARTICLE_COUNT.toString()  + ", 受影响的行数不等于 1 .");
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_ARTICLE_COUNT));
        }

        // 7、更新成功
    }

    /**
     * 将用户的对应帖子数进行减一操作
     * @param id
     */
    @Override
    public void subOneArticleCountById(Long id) {
        // 检验参数
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 获取用户数据，检验
        User existsUser = userMapper.selectByPrimaryKey(id);
        if (existsUser == null) {
            // 打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString() + ", user id = " + id);
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_NOT_EXISTS));
        }

        // 创建一个需要更新的对象
        User user = new User();
        user.setId(existsUser.getId());
        user.setArticleCount(existsUser.getArticleCount() - 1);
        // 判断减1之后，用户的发帖数是否小于0
        if (user.getArticleCount() < 0) {
            // 如果小于0，则设置为0
            user.setArticleCount(0);
        }

        // 调用DAO层
        int row = userMapper.updateByPrimaryKeySelective(user);
        if (row != 1) {
            // 打印日志
            log.warn(ResultCode.FAILED_USER_ARTICLE_COUNT.toString()  + ", 受影响的行数不等于 1 .");
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_ARTICLE_COUNT));
        }
        // 更新成功
    }

    /**
     * 修改用户信息
     * @param user
     */
    @Override
    public void modifyInfo(User user) {
        // 非空校验
        if (user == null || user.getId() == null || user.getId() <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 校验用户是否存在
        User existsUser = userMapper.selectByPrimaryKey(user.getId());
        if (existsUser == null) {
            // 打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_NOT_EXISTS));
        }

        // 3. 定义一个标志位
        boolean checkAttr = false; // false表示没有校验通过，用来校验传来的数值是否是存在修改操作

        // 4. 定义一个专门用来更新的对象，防止用户传入的User对象设置了其他的属性
        // 当使用动态SQL进行更新的时候，覆盖了没有经过校验的字段
        User updateUser = new User();
        // 5. 设置用户Id
        updateUser.setId(user.getId());

        // 6、对每一个信息进行校验
        if(!StringUtil.isEmpty(user.getUserName())
                && !user.getUserName().equals(existsUser.getUserName())) {
            // 需要更新用户名(登录名)时，进行唯一性的校验
            User checkUser = userMapper.selectByUserName(user.getUserName());
            if (checkUser != null) {
                // 用户已存在
                log.warn(ResultCode.FAILED_USER_EXISTS.toString());
                // 抛出异常
                throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_EXISTS));
            }
            // 不存在，进行更新
            updateUser.setUserName(user.getUserName());
            checkAttr = true;
        }

        // 7. 校验昵称
        if (!StringUtil.isEmpty(user.getNickName())
                && !user.getNickName().equals(existsUser.getNickName())) {
            // 设置昵称
            updateUser.setNickName(user.getNickName());
            // 更新标志位
            checkAttr = true;
        }
        // 8. 校验性别
        if (user.getGender() != null && user.getGender() != existsUser.getGender()) {
            // 设置性别
            updateUser.setGender(user.getGender());
            // 合法性校验
            if (updateUser.getGender() > 2 || updateUser.getGender() < 0) {
                updateUser.setGender((byte) 2);
            }
            // 更新标志位
            checkAttr = true;
        }
        // 9. 校验邮箱
        if (!StringUtil.isEmpty(user.getEmail())
                && !user.getEmail().equals(existsUser.getEmail())) {
            // 设置邮箱
            updateUser.setEmail(user.getEmail());
            // 更新标志位
            checkAttr = true;
        }
        // 9. 校验电话号码
        if (!StringUtil.isEmpty(user.getPhoneNumb())
                && !user.getPhoneNumb().equals(existsUser.getPhoneNumb())) {
            // 设置电话号码
            updateUser.setPhoneNumb(user.getPhoneNumb());
            // 更新标志位
            checkAttr = true;
        }
        // 10. 校验个人简介
        if (!StringUtil.isEmpty(user.getRemark())
                && !user.getRemark().equals(existsUser.getRemark())) {
            // 设置电话号码
            updateUser.setRemark(user.getRemark());
            // 更新标志位
            checkAttr = true;
        }

        // 11、检验白哦职位是否是 true，如果为true就进行修改
        if (checkAttr == false) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 12、进行更改，调用DAO层
        int row = userMapper.updateByPrimaryKeySelective(updateUser);
        if (row != 1) {
            log.warn(ResultCode.FAILED.toString() + ", 受影响的行数不等于 1 .");

            throw new ApplicationException(AppResult.fail(ResultCode.FAILED));
        }
    }

    /**
     * 修改密码
     * @param request
     */
    @Override
    public void modifyPassword(UserModPasswordRequest request) {
        // 校验参数
        if (request == null
                || StringUtil.isEmpty(request.getOldPassword())
                || StringUtil.isEmpty(request.getNewPassword())) {
            // 发生错误
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        Long userId = JWTUtil.getUserIdFromToken(Constants.JWT);
        //查询用户信息，根据用户ID
        User user = userMapper.selectByPrimaryKey(userId);
        if (user == null
                || user.getDeleteState().equals(Constants.IS_DELETE_STATE)) {
            // 没有这个用户信息
            // 打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_USER_NOT_EXISTS));
        }

        // 判断 老密码是否是数据库中的密码
        String oldPasswordEntry = MD5Util.md5Salt(request.getOldPassword(), user.getSalt());
        if (!oldPasswordEntry.equalsIgnoreCase(user.getPassWord())) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.fail(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 到这里说明密码一致，更改成新的密码
        // 生成新的盐值
        String salt = UUIDUtil.UUID_32();
        // 根据盐值生成新的密文
        String entryPassword = MD5Util.md5Salt(request.getNewPassword(), salt);

        // 构造需要更新的对象
        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setPassWord(entryPassword);
        updateUser.setSalt(salt);
        updateUser.setUpdateTime(new Date());

        // 调用DAO层
        int row = userMapper.updateByPrimaryKeySelective(updateUser);
        if (row != 1) {
            log.warn(ResultCode.FAILED.toString() + ", 受影响的行数不等于 1 .");

            throw new ApplicationException(AppResult.fail(ResultCode.FAILED));
        }
    }
}
